/*
 *Copyright (c) [2019-2020]  C2comm, Inc.  All rights reserved.
 *
 *This program is free software; you can redistribute it and/or
 *modify it under the terms of the GNU General Public License
 *as published by the Free Software Foundation; either version 2
 *of the License, or (at your option) any later version.
 *
 *This program is distributed in the hope that it will be useful,
 *but WITHOUT ANY WARRANTY; without even the implied warranty of
 *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *GNU General Public License for more details.
 *
 *You should have received a copy of the GNU General Public License
 *along with this program; If not, see <https://www.gnu.org/licenses/>
 */

/*
 *Vendor: C2comm
 *Version: 1.0
 *Filename: SMPermntDelay.v
 *Target Device: Altera
 *Author : sunxiaotian@c2comm.cn
*/

`timescale 1ns / 1ps
module SMPermntDelay(
	input wire SYS2CORE_clk,
	input wire SYS2CORE_rst_n,
	input  wire [47:0]  DC2CORE_device_clk,
	input wire [348:0] CONF2CORE_sm_static_conf,
	input wire PPM2SM_pcf_rx_cb_wr,
	input wire [85:0] PPM2SCM_pcf_rx_cb,
	output wire PPM2SM_pcf_rx_cb_ready,
	
	input wire [19:0] G_local_clk,
	
	input wire SC2PD_pcf_des_ready, 
	output reg [90:0] G_pcf_des,
	output reg G_pcf_des_valid,
	
	input wire [76:0] G_current_state
);

/*//////////////////////////////////////////////////////////
                    中间变量声明区域
*///////////////////////////////////////////////////////////
//本模块中所有中间变量(wire/reg/parameter)在此集中声明 


wire        ppmbuffer_wrreq;
wire [85:0] ppmbuffer_wdata;
reg         ppmbuffer_wrack;



reg  [ 7:0] dlybuffer_ivalid;
reg  [85:0] dlybuffer_idata;
wire [ 7:0] dlybuffer_iready;

wire [ 7:0] dlybuffer_owrreq;
wire [85:0] dlybuffer_odata [7:0];
reg  [ 7:0] dlybuffer_owrack;

reg  [ 7:0] cs_dly_done;
reg  [ 7:0] ca_dly_done;
reg  [ 7:0] in_dly_done;

reg  [ 7:0] sel_dly_req;

reg         iready_get;
wire [ 7:0] iready_grant_prior;

reg         seldone_get;
wire [ 7:0] seldone_grant_prior;


wire [85:0] precal_sel_data [7:0]; 
wire [ 7:0] invert_matrix [85:0];
wire [85:0] postcal_sel_odata;

wire [3:0] pcf_des_membersum;

localparam  PCF_CS        = 2'd0,
            PCF_CA        = 2'd1,
            PCF_IN        = 2'd2; 



always @(posedge SYS2CORE_clk or negedge SYS2CORE_rst_n) begin
    if(!SYS2CORE_rst_n) begin
        ppmbuffer_wrack   <= 1'b0;
        dlybuffer_ivalid <= {8{1'b0}};
        iready_get       <= 1'b0;
    end
    else begin
        if((ppmbuffer_wrreq == 1'b1) && ((|iready_grant_prior) == 1'b1))begin
            ppmbuffer_wrack   <= 1'b1;
            dlybuffer_ivalid <= iready_grant_prior;
            dlybuffer_idata  <= ppmbuffer_wdata;
            iready_get       <= 1'b1;
        end
        else begin
            ppmbuffer_wrack   <= 1'b0;
            dlybuffer_ivalid <= {8{1'b0}};
            iready_get       <= 1'b0;
        end
    end
end


generate
    genvar i;
    for(i=0;i<8;i=i+1) begin:dly_result_mon
        always @* begin:dm
            if((dlybuffer_owrreq[i] == 1'b1) && 
               (dlybuffer_odata[i][85:38] <= DC2CORE_device_clk)) begin
             
                case(dlybuffer_odata[i][17:16])
                    PCF_CS: begin
                        cs_dly_done[i] = 1'b1;
                        ca_dly_done[i] = 1'b0;
                        in_dly_done[i] = 1'b0;
                    end
                    
                    PCF_CA: begin
                        cs_dly_done[i] = 1'b0;
                        ca_dly_done[i] = 1'b1;
                        in_dly_done[i] = 1'b0;
                    end
                    
                    PCF_IN: begin
                        cs_dly_done[i] = 1'b0;
                        ca_dly_done[i] = 1'b0;
                        in_dly_done[i] = 1'b1;
                    end
                    
                    default: begin
                        cs_dly_done[i] = 1'b0;
                        ca_dly_done[i] = 1'b0;
                        in_dly_done[i] = 1'b0;
                    end
                endcase
            end
            else begin
                cs_dly_done[i] = 1'b0;
                ca_dly_done[i] = 1'b0;
                in_dly_done[i] = 1'b0;
            end
        end
    end
endgenerate



always @* begin
    if((|cs_dly_done) == 1'b1)
        sel_dly_req = cs_dly_done;
    else if((|ca_dly_done) == 1'b1)
        sel_dly_req = ca_dly_done;
    else
        sel_dly_req = in_dly_done;
end

/*通过倒置矩阵计算，根据优先级仲裁得到的seldone_grant_prior选择有效buffer的输出
    步骤1、通过seldone_grant_prior对全部buffer的输出信号进行预处理
       将seldone_grant_prior中bit为0的位对应的buffer输出全部清零，仅保留bit为1的位的输出
       例如seldone_grant_prior==8'b0000_0010,则仅保留dlybuffer_odata[1]的值，其余全部清零
       
    步骤2、将预处理的结果作为矩阵进行导致倒置，即行(row)/列(col)互换，方便后续进行计算
    
    步骤3、将倒置的矩阵的行(row)进行按位或操作，由于或操作的特性，即可得到有效输出
            如果全为0，则该bit为0
            只要有1bit为1(由于步骤1对其他信号进行了清零，也只会出现1bit为1)，则经过计算会保留该位的1
*/
//步骤1
generate
    genvar x;
    for(x=0;x<8;x=x+1) begin:pre_calulate
        assign precal_sel_data[x] = (seldone_grant_prior[x] == 1'b1) ? dlybuffer_odata[x] : {8{1'b0}};
    end
endgenerate

//步骤2
generate
    genvar row,col;
    for(row=0;row<8;row=row+1) begin:invert_magic_row
        for(col=0;col<86;col=col+1) begin:invert_magic_col
            assign invert_matrix[col][row] = precal_sel_data[row][col];
        end
    end
endgenerate

//步骤3
generate
    genvar y;
    for(y=0;y<86;y=y+1) begin:post_calulate
        assign postcal_sel_odata[y] = (|invert_matrix[y]);
    end
endgenerate


assign pcf_des_membersum = postcal_sel_odata[15] + 
                           postcal_sel_odata[14] + 
                           postcal_sel_odata[13] + 
                           postcal_sel_odata[12] + 
                           postcal_sel_odata[11] + 
                           postcal_sel_odata[10] + 
                           postcal_sel_odata[ 9] + 
                           postcal_sel_odata[ 8];


always @(posedge SYS2CORE_clk or negedge SYS2CORE_rst_n) begin
    if(SYS2CORE_rst_n == 1'b0) begin
        seldone_get  <= 1'b0;
        G_pcf_des_valid <= 1'b0;
        dlybuffer_owrack <= {8{1'b0}};
		G_pcf_des <= 91'b0;
    end
    else begin
        if((SC2PD_pcf_des_ready == 1'b1) && ((|seldone_grant_prior) == 1'b1)) begin
      
            seldone_get  <= 1'b1;
            dlybuffer_owrack <= seldone_grant_prior;
            
            G_pcf_des_valid <= 1'b1;

			//Type+Membership+Intergration Cycle+ChannelID
            G_pcf_des[17: 0] <= postcal_sel_odata[17:0];
            G_pcf_des[37:18] <= postcal_sel_odata[37:18];
			G_pcf_des[90:43] <= postcal_sel_odata[85:38];
            
            //Detect_vector
			G_pcf_des[38] <= pcf_des_membersum >= CONF2CORE_sm_static_conf[143:140] && pcf_des_membersum < CONF2CORE_sm_static_conf[147:144];
			G_pcf_des[39] <= pcf_des_membersum >= CONF2CORE_sm_static_conf[147:144];
			G_pcf_des[40] <= pcf_des_membersum >= CONF2CORE_sm_static_conf[135:132];
			G_pcf_des[41] <= pcf_des_membersum < CONF2CORE_sm_static_conf[135:132] && pcf_des_membersum>=CONF2CORE_sm_static_conf[131:128];
			G_pcf_des[42] <= 1'b1;
            
        end
        else begin
            seldone_get  <= 1'b0;
            G_pcf_des_valid <= 1'b0;
            dlybuffer_owrack <= {8{1'b0}};
			G_pcf_des <= 91'b0;
        end
    end
end
   


InportBuffer #(
    .DWIDTH(86)
)PPMBuffer_inst(
.clk(SYS2CORE_clk),
.rst_n(SYS2CORE_rst_n),
           
.idata_valid(PPM2SM_pcf_rx_cb_wr),
.idata(PPM2SCM_pcf_rx_cb),
.idata_ready(PPM2SM_pcf_rx_cb_ready),
    
.odata_wrreq(ppmbuffer_wrreq),
.odata(ppmbuffer_wdata),
.odata_wrack(ppmbuffer_wrack)
);

prior_grant #(
    .GRANT_WIDTH(8),
    .PRIOR_LOW_OR_HIGH(0)
)iready_grant_inst(
    .clk(SYS2CORE_clk),
    .rst_n(SYS2CORE_rst_n),
    
    .req(dlybuffer_iready),
    
    .get(iready_get),
    
    .fix_prior(8'b11111111),
    
    .grant_prior(iready_grant_prior)
);

prior_grant #(
    .GRANT_WIDTH(8),
    .PRIOR_LOW_OR_HIGH(0)
)seldone_grant_inst(
    .clk(SYS2CORE_clk),
    .rst_n(SYS2CORE_rst_n),
    
    .req(sel_dly_req),
    
    .get(seldone_get),
    
    .fix_prior(8'b11111111),
    
    .grant_prior(seldone_grant_prior)
);


generate 
	genvar sum;
	for(sum=0;sum<8;sum=sum+1) begin:dlybuffer
		InportBuffer #(
			.DWIDTH(86)
		)InportBuffer_inst(
		.clk(SYS2CORE_clk), 
		.rst_n(SYS2CORE_rst_n),
		.idata_valid(dlybuffer_ivalid[sum]),
		.idata(dlybuffer_idata),
		.idata_ready(dlybuffer_iready[sum]), 
		.odata_wrreq(dlybuffer_owrreq[sum]),
		.odata(dlybuffer_odata[sum]),
		.odata_wrack(dlybuffer_owrack[sum]) 
		);
	end
endgenerate
endmodule